package ro.flashbite.sound
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.media.SoundTransform;
	
	import ro.flashbite.helpers.MathHelpers;
	import ro.flashbite.interfaces.IDestroyable;
	import ro.flashbite.loggers.ILogger;
	import ro.flashbite.loggers.LoggerFactory;

	public final class Sound2 extends EventDispatcher implements IDestroyable
	{
		private var _sound			:	Sound;
		private var _id				:	String;
		private var _soundChannelID	:	String;
		
		private var _soundChannel	:	SoundChannel;
		
		private var _isPaused		:	Boolean;
		private var _isPlaying		:	Boolean;
		private var _isStopped		:	Boolean;
		
		private var _pausePosition	:	Number;
		
		private var _logger			:	ILogger;
		
		private var _soundTransform	:	SoundTransform;
		
		private var _peak			:	Number;
		
		//==================================================================================================
		// CONSTRUCTOR
		//==================================================================================================
		
		public function Sound2() { super();	}
		
		//==================================================================================================
		// Proper Constructor
		//==================================================================================================
		
		public function init(sound:Sound, id:String, soundChannelID:String = SoundChannelIds.VOICE):void
		{
			_sound = sound;
			_id = id;
			_soundChannelID = soundChannelID;
			
			//internals
			_pausePosition = 0;
			
			_logger = LoggerFactory.getLogger("Sound2 - " + id);
		}
		
		//==================================================================================================
		// IDestroy
		//==================================================================================================
		
		public function destroy():void
		{
			//pause();
			stopChannel();
			
			_soundChannelID = null;
			_id = null;
			_sound = null;
			_logger = null;
			
			_soundTransform = null;
		}
		
		//==================================================================================================
		// Player Behaviour
		//==================================================================================================
		
		public function play():Boolean
		{
			//stop if already playing
			stop();
			//start the sound
			if (startChannel()) {
				//mark as playing
				_isPlaying = true;
				//send start event
				dispatchEvent(new Sound2Event(Sound2Event.STARTED));
				_logger.debug("Starting....");
				
				//debugging only
				/*var e:Error = new Error();
				if (e) {
				_logger.debug(e.getStackTrace());
				}*/
				return true;
			}
			return false;
		}
		
		public function pause():void
		{
			//if isn't playing, no pause to do 
			if (_isPlaying == false) return;
			 //saving pause location
			_pausePosition = MathHelpers.clamp(getSoundPosition(), 0, getLength() - 100);
			//stop the sound
			stopChannel();
			//mark as paused
			_isPaused = true;
			
			_logger.debug("pause....");
		}
		
		public function resume():void
		{
			//the resume function is used to resume a paused sound
			if (_isPaused == false) return;
			//replay the sound from last saved location in so
			startChannel(_pausePosition);
			//reset pause position
			_pausePosition = 0;
			//mark as unpaused
			_isPaused = false;
			
			_logger.debug("resume....");
		}
		
		public function stop():void
		{
			//we must stop a sound only if it's playing or it's paused
			if (_isPlaying) {
				//reset pause position
				_pausePosition = 0;
				//real stop of sound
				stopChannel();
				//dispatch -> no more...
				//dispatchEvent(new Sound2Event(Sound2Event.COMPLETE));
			}
			_isPlaying = false;
		}
		
		override public function toString():String
		{
			return "Sound2-" + _id;
		}
		
		//==================================================================================================
		// PRIVATE
		//==================================================================================================
		
		private function startChannel(position:int = 0):Boolean
		{
			//real start of playing sound on position
			var posToStartSound:Number = getSoundPosition();
			_logger.debug("startChannel(posToStartSound = " + posToStartSound + ");");
			_soundChannel = _sound.play(posToStartSound, 1, _soundTransform);
			if (_soundChannel) {
				_soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete, false, 0, true);
				return true
			} else {
				_logger.error("This couldn't start. No sound channel available");
				return false;
			}
		}
		
		private function stopChannel():void
		{
			//real stop of sound
			if (_soundChannel) {
				_soundChannel.removeEventListener(Event.SOUND_COMPLETE, onSoundComplete);
				_soundChannel.stop();
				_soundChannel = null;
			}
		}
		
		private function onSoundComplete(e:Event):void
		{
			//stop the channel
			stopChannel();
			//send completion
			dispatchEvent(new Sound2Event(Sound2Event.COMPLETE));
		}
		
		private function getSoundPosition():Number
		{
			if (_soundChannel)
				return _soundChannel.position;
			else return _pausePosition;
		}
		
		//==================================================================================================
		// GETTERS
		//==================================================================================================
		
		public function getLength():uint
		{
			if (_sound) return _sound.length;
			return 0;
		}
		
		public function isPlaying():Boolean {return (_soundChannel != null && _isPlaying); }
		
		public function isPaused():Boolean { return (_pausePosition >= 0 && _isPaused); }
		
		public function getId():String { return _id; }
		
		public function getAmplitude():Number
		{
			if (this.isPlaying())
				return (_soundChannel.leftPeak + _soundChannel.rightPeak) / 2;
			return -1;
		}

		public function get soundChannelID():String { return _soundChannelID; }
		
		public function set soundTransform(v:SoundTransform):void 
		{ 
			_soundTransform = v;
			if (this.isPlaying()) {
				//apply also sound transform to it
				_soundChannel.soundTransform = _soundTransform;
			}
		}

		public function get peak()				:	Number 	{	return _peak; 	}
		public function set peak(value:Number)	:	void 	{ _peak = value; 	}
	}
}